// 版权归Go作者所有。版权所有。
// 此源代码的使用受BSD样式的约束
// 可以在许可证文件中找到的许可证。

package s390x

import (
	"math/bits"
)

// 旋转图表示“旋转”所需的立即数
// 然后选定位指令”。
// None
// 起始值和结束值是表示
// 遮罩区域。它们是包容性的，而且规模很大-
// endian顺序（位0为MSB，位63为LSB）。他们
// 我可以绕过去。
// None
// 一些例子：
// None
// 遮罩区域|开始|结束
// --------------------------+-------+----
// 0x00_00_00_00_00_00_00_0f | 60 | 63
// 0xf0_00_00_00_00_00_00_00 | 0 | 3
// 0xf0_00_00_00_00_00_00_00_0f | 60 | 3
// None
// “数量”值表示要旋转的数量
// 留下的输入。请注意，执行此旋转
// 在使用遮罩区域之前。
type RotateParams struct {
	Start  uint8 // 大端起始位索引[0..63]
	End    uint8 // 大端位索引[0..63]
	Amount uint8 // 向左旋转的数量
}

// NewRotateParams创建一组表示
// 由所提供的量和位的选择所留下的旋转
// 在提供的开始索引和结束索引之间（包括）。
// None
// 开始索引和结束索引以及旋转量必须全部
// 在0-63（含0-63）范围内，否则此函数将死机。
func NewRotateParams(start, end, amount uint8) RotateParams {
	if start&^63 != 0 {
		panic("start out of bounds")
	}
	if end&^63 != 0 {
		panic("end out of bounds")
	}
	if amount&^63 != 0 {
		panic("amount out of bounds")
	}
	return RotateParams{
		Start:  start,
		End:    end,
		Amount: amount,
	}
}

// RotateLeft使用旋转量生成一组新的参数
// 按给定值增加。所选位保持不变。
func (r RotateParams) RotateLeft(amount uint8) RotateParams {
	r.Amount += amount
	r.Amount &= 63
	return r
}

// OutMask提供表示所选位的掩码。
func (r RotateParams) OutMask() uint64 {
	// 注意：对于引导编译器，z必须是未签名的
	z := uint8(63-r.End+r.Start) & 63 // 掩码中的零位数
	return bits.RotateLeft64(^uint64(0)<<z, -int(r.Start))
}

// InMask提供一个表示所选位的掩码
// 到源值（即预旋转）。
func (r RotateParams) InMask() uint64 {
	return bits.RotateLeft64(r.OutMask(), -int(r.Amount))
}

// OutMerge尝试生成一组表示
// 所选位与提供的掩码之间的交集。
// 如果交点不可表示（0或不连续），则为零
// 将被退回。
func (r RotateParams) OutMerge(mask uint64) *RotateParams {
	mask &= r.OutMask()
	if mask == 0 {
		return nil
	}

	// 规范化掩码，使设置位左对齐
	o := bits.LeadingZeros64(^mask)
	mask = bits.RotateLeft64(mask, o)
	z := bits.LeadingZeros64(mask)
	mask = bits.RotateLeft64(mask, z)

	// 检查规范化掩码是否连续
	l := bits.LeadingZeros64(^mask)
	if l+bits.TrailingZeros64(mask) != 64 {
		return nil
	}

	// 更新开始和结束位置（旋转量保持不变）
	r.Start = uint8(o+z) & 63
	r.End = (r.Start + uint8(l) - 1) & 63
	return &r
}

// InMerge尝试生成一组表示
// 所选位与提供的掩码之间的交集
// 应用于源值（即预旋转）。
// 如果交点不可表示（0或不连续），则为零
// 将被退回。
func (r RotateParams) InMerge(mask uint64) *RotateParams {
	return r.OutMerge(bits.RotateLeft64(mask, int(r.Amount)))
}

func (RotateParams) CanBeAnSSAAux() {}
